using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Net.Mail;
using System.Text;
using System.Threading;
using System.Web;

namespace BoxBinary.AspectCache.Core.ErrorLogging
{
    public class PeriodicErrorSummaryThreadWorker
    {
        private static readonly Collection<ErrorSummary> _inMemoryExceptionCache = new Collection<ErrorSummary>();
        private bool _isRunning;
        private int _pollIntervalSeconds = 30;

        public bool IsRunning
        {
            get { return _isRunning; }
        }

        public static Collection<ErrorSummary> InMemoryExceptionCache
        {
            get { return _inMemoryExceptionCache; }
        }

        /// <summary>
        /// Method to start this worker monitoring the raised errors, sending an email
        /// summary when a threshold is hit
        /// </summary>
        public void Start()
        {
            _isRunning = true;
            try
            {
                while (IsRunning)
                {
                    lock (InMemoryExceptionCache)
                    {
                        // Check to see if there are any new errors that we need to send an email alert for
                        int newExceptionsSinceLastCheck = InMemoryExceptionCache.Count;

                        // If more than x exceptions have been raised in the last check time, then
                        // we send an alert summary in case we have a major problem
                        if (newExceptionsSinceLastCheck > 10)
                        {
                            try
                            {
                                var message = new MailMessage();
                                message.From = new MailAddress(ConfigSection.Current.EmailFromAddress);
                                message.To.Add(new MailAddress(ConfigSection.Current.EmailLowPriorityDevelopersAddress));
                                message.Subject =
                                    string.Format("Error frequency alert on {0} ({1})",
                                                  Environment.MachineName,
                                                  Path.GetDirectoryName(HttpRuntime.AppDomainAppPath));
                                message.IsBodyHtml = true;

                                var buildSummary = new StringBuilder();
                                buildSummary.Append(
                                                       "<style>body,p,h1,h2,h3,td,tr,ul,li {font-family:Verdana;font-weight:normal}body{color:#400;background-color:#eee;font-size:67.5%}; h1 {font-size:14px}</style>");
                                buildSummary.AppendFormat(
                                                             "<h1>Error monitor: threshold exceeded for '{0}'<br> at {1}</h1>",
                                                             HttpRuntime.AppDomainAppPath, DateTime.Now.ToString("r"));
                                buildSummary.Append(
                                                       "<p>You have been sent this summary because an unusual amount of errors have occurred on the web server.</p>");
                                buildSummary.Append(GetErrorSumarries());

                                message.Body = buildSummary.ToString();

                                var client = new SmtpClient();
                                client.Send(message);
                            }
                            catch
                            {
                                /* Ignore error 
                                 * TODO: perform critical logging
                                 */
                            }
                        }

                        // Garbage collect the exceptions as we no longer need to keep track of them
                        GarbageCollect();
                    }
                    // Sleep for a while until we next check
                    Thread.Sleep(TimeSpan.FromSeconds(_pollIntervalSeconds));
                }
            }
            catch (ThreadAbortException)
            {
                _isRunning = false;
                GarbageCollect();
            }
        }

        internal string GetErrorSumarries()
        {
            var aggregateErrorTypes = new Dictionary<string, int>(InMemoryExceptionCache.Count);
            foreach (ErrorSummary summary in InMemoryExceptionCache)
            {
                Exception thisEx = summary.Exception;

                string hostInfo = string.Empty;
                // Get the source website of the error:
                if (summary.ErrorMetaData.ContainsKey(ErrorLoggerBase.MD_Host))
                    hostInfo = string.Format("Host: <i>{0}</i>, ", summary.ErrorMetaData[ErrorLoggerBase.MD_Host]);

                // Filter the HttpUnhandledExceptions to show the real exception raised
                if (thisEx is HttpUnhandledException && thisEx.InnerException != null)
                    thisEx = thisEx.InnerException;

                string errorType = string.Format("{0} Type: <i>{1}</i> ('{2}')", hostInfo, thisEx.GetType().FullName,
                                                 thisEx.Message);
                if (!aggregateErrorTypes.ContainsKey(errorType))
                    aggregateErrorTypes.Add(errorType, 1);
                else
                    aggregateErrorTypes[errorType] += 1;
            }

            var buildSummary = new StringBuilder();
            buildSummary.AppendFormat("<p><i>Number of errors in last {0} minute(s): {1}</i>",
                                      TimeSpan.FromSeconds(_pollIntervalSeconds).TotalMinutes.ToString("0.0"),
                                      InMemoryExceptionCache.Count);
            buildSummary.Append("<h3>Summary of most common errors:</h3>\n<ul>");
            foreach (var type in aggregateErrorTypes)
                buildSummary.AppendFormat("<li><b>{0} occurences</b> of {1}</li>", type.Value, type.Key);
            buildSummary.Append("\n</ul>");
            return buildSummary.ToString();
        }

        internal void GarbageCollect()
        {
            // Dispose things nicely here if necessary
            foreach (ErrorSummary summary in InMemoryExceptionCache)
            {
                if (summary != null && summary.Exception is IDisposable)
                    ((IDisposable) summary.Exception).Dispose();
            }
            InMemoryExceptionCache.Clear();
        }
    }
}